Micron Document
rns.moscow 🟥 [git]

Commit fcbb4bff7bbe22d901da1fcd96a8d5dcc7242aef


Parents : 882a897
Author : Nickie Deuxyeux <nikolay@dvoeglazov.ru>
Date : 2026-07-05T06:25:36+03:00

bot: wait for a path before relaying points to the aggregator

relay_point() only checked Identity.recall() before calling Packet.send(),
same silent-drop hazard already fixed for ping/probe replies: identity known
doesn't mean a path is known, and send() doesn't raise either way. An
aggregator outage long enough for its path to expire elsewhere on the network
left every relay going out logged as sent but never actually delivered.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Changes

1 files changed, 36 insertions(+), 4 deletions(-)


Diff

diff --git a/bot/wardrive_bot.py b/bot/wardrive_bot.py
index e2d58c4..012da3d 100755
--- a/bot/wardrive_bot.py
+++ b/bot/wardrive_bot.py
@@ -60,6 +60,12 @@ PROBE_REPLY_SIZE = struct.calcsize(PROBE_REPLY_FMT)
PING_PAYLOAD_FMT = ">16s"
PING_PAYLOAD_SIZE = struct.calcsize(PING_PAYLOAD_FMT)
+# rnsd only auto-discovers a path to a destination if it happens to have heard a
+# recent-enough announce for it — not guaranteed, and Packet.send() won't raise
+# just because there's nowhere to send the packet. Used by both the ping/probe
+# reply helper and the aggregator relay helper below.
+REPLY_PATH_WAIT_SECS = 10
+
BOT_CONFIG_PATH = "/etc/reticulum/wardrive_bot.json"
def _load_bot_config():
@@ -178,6 +184,28 @@ def insert_point(con, receiver_hash, sender_hash, lat, lon, rssi, snr, q, source
# Relay
# ---------------------------------------------------------------------------
+def _send_relay_with_path(dest, payload, log_suffix, timeout=REPLY_PATH_WAIT_SECS):
+ # Same silent-drop hazard as replies (see the reply helper further down):
+ # identity known doesn't mean a path is known, and an aggregator outage long
+ # enough for its path to expire elsewhere on the network left this sending
+ # silently into the void, with a "REL ..." log line regardless of delivery.
+ if not RNS.Transport.has_path(dest.hash):
+ RNS.Transport.request_path(dest.hash)
+ waited = 0.0
+ while not RNS.Transport.has_path(dest.hash) and waited < timeout:
+ time.sleep(0.5)
+ waited += 0.5
+
+ if not RNS.Transport.has_path(dest.hash):
+ log.warning(f"REL {log_suffix} — no path after requesting, giving up")
+ return
+
+ try:
+ RNS.Packet(dest, payload).send()
+ log.info(f"REL {log_suffix}")
+ except Exception as e:
+ log.warning(f"Relay failed: {e}")
+
def relay_point(lat, lon, rssi, snr, q, sender_hash):
if not RELAY_HASH:
return
@@ -193,12 +221,16 @@ def relay_point(lat, lon, rssi, snr, q, sender_hash):
q_val = round(q) if q is not None else _INT_NULL
payload = struct.pack(RELAY_PAYLOAD_FMT, lat, lon, rssi_val, snr_val, q_val, bytes(sender_hash))
dest = RNS.Destination(identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "wardrive", "aggregator")
- RNS.Packet(dest, payload).send()
rel_parts = [f"Lat: {lat:.5f}", f"Lon: {lon:.5f}"]
if rssi is not None: rel_parts.append(f"RSSI: {round(rssi)}dBm")
if snr is not None: rel_parts.append(f"SNR: {snr}dB")
if q is not None: rel_parts.append(f"Q: {round(q)}")
- log.info(f"REL dst={RNS.prettyhexrep(agg_hash)} [{', '.join(rel_parts)}]")
+ log_suffix = f"dst={RNS.prettyhexrep(agg_hash)} [{', '.join(rel_parts)}]"
+ threading.Thread(
+ target=_send_relay_with_path,
+ args=(dest, payload, log_suffix),
+ daemon=True,
+ ).start()
except Exception as e:
log.warning(f"Relay failed: {e}")
@@ -427,8 +459,8 @@ def handle_collector_packet(reticulum, db, receiver_hash):
# is a one-shot unicast send with no retry, so a missing path silently eats it.
# Mirror what the wardrive app itself does when it wants to reach the bot:
# explicitly request a path and wait briefly before giving up.
-
-REPLY_PATH_WAIT_SECS = 10
+# (REPLY_PATH_WAIT_SECS is defined up near the payload format constants, shared
+# with the aggregator relay helper.)
def _send_reply_with_path(dest, payload, kind, source_hex, timeout=REPLY_PATH_WAIT_SECS):
if not RNS.Transport.has_path(dest.hash):

Served by rngit 1.4.2 - Generated in 0.02s